﻿using System;
using System.Threading;
using System.Reflection;

using Heaven.Graphics;
using Microsoft.Xna.Framework.Graphics;
using XNA = Microsoft.Xna.Framework.Graphics;

namespace Heaven.Xna.Graphics
{
    /// <summary>
    /// XNA Render target 2D
    /// </summary>
    internal class XnaRenderTarget
    {
        #region Атрибуты

        // XNA рендер таргет
        XNA.RenderTarget2D renderTarget = null;
        // XNA текстура
        XNA.Texture2D texture = null;
        // Графическое устройство
        XNA.GraphicsDevice device = null;
        // Depth-stensil buffer for our render target
        DepthStencilBuffer depthStencilBuffer = null;
        // Original depth-stensil buffer
        DepthStencilBuffer originalDepthStencilBuffer = null;

        #endregion

        #region Свойства

        /// <summary>
        /// Ширина (в пикселях)
        /// </summary>
        public int Width
        {
            get
            {
                return renderTarget.Width;
            }
        }

        /// <summary>
        /// Высота (в пикселях)
        /// </summary>
        public int Height
        {
            get { return renderTarget.Height; }
        }

        /// <summary>
        /// Формат пикселей текстуры
        /// </summary>
        public SurfaceFormat Format
        {
            get { return renderTarget.Format; }
        }

        /// <summary>
        /// Внутренняя текстура XNA
        /// </summary>
        internal XNA.Texture2D XnaTexture
        {
            get
            {
                //Validate();
                return texture;
            }
        }

        #endregion

        #region Инициализация

        internal XnaRenderTarget(
            XNA.GraphicsDevice device,
            int width, int height,
            SurfaceFormat format)
        {
            this.device = device;
            renderTarget = new XNA.RenderTarget2D(device,
                width, height, 1, format,
                MultiSampleType.None,
                0, RenderTargetUsage.PreserveContents);
            depthStencilBuffer = new DepthStencilBuffer(
                device, width, height, device.DepthStencilBuffer.Format,
                MultiSampleType.None,
                0);

            device.DeviceReset += new EventHandler(OnDeviceReset);
        }

        #endregion

        #region Обработка событий

        void OnDeviceReset(object sender, EventArgs e)
        {
            if (!renderTarget.IsDisposed) renderTarget.Dispose();

            renderTarget = new RenderTarget2D(device,
                                              renderTarget.Width,
                                              renderTarget.Height, 1,
                                              renderTarget.Format,
                                              MultiSampleType.None,
                                              0, RenderTargetUsage.PreserveContents);

            if ((texture != null) && (!texture.IsDisposed)) texture.Dispose();
            texture = null;

            if (!depthStencilBuffer.IsDisposed) depthStencilBuffer.Dispose();

            depthStencilBuffer = new DepthStencilBuffer(
                device, Width, Height, device.DepthStencilBuffer.Format,
                MultiSampleType.None,
                0);
        }

        #endregion

        #region Деструктор

        ~XnaRenderTarget()
        {
            Dispose();
        }

        internal void Dispose()
        {
            device.DeviceReset -= new EventHandler(OnDeviceReset);
            if ((renderTarget != null)&&(!renderTarget.IsDisposed)) renderTarget.Dispose();
            if ((texture != null) && (!texture.IsDisposed)) texture.Dispose();
            if ((depthStencilBuffer != null)&&(!depthStencilBuffer.IsDisposed)) depthStencilBuffer.Dispose();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Begins using this render 
        /// target to render into it
        /// </summary>
        public void BeginRendering()
        {
            // Setting render target
            device.SetRenderTarget(0, renderTarget);
            // Backup original depth stencil buffer and set our buffer
            originalDepthStencilBuffer = device.DepthStencilBuffer;
            device.DepthStencilBuffer = depthStencilBuffer;
        }

        /// <summary>
        /// Ends using this render 
        /// target to render into it
        /// </summary>
        public void EndRendering()
        {
            // Ending rendering
            // Get back to main buffer
            device.SetRenderTarget(0, null);
            // Restore proper depth buffer
            device.DepthStencilBuffer = originalDepthStencilBuffer;

            // Retrieving contents of 
            // render target to our texture
            texture = renderTarget.GetTexture();
        }

        /*void Validate()
        {
            if (texture == null)
            {
                throw new Exception("There was no rendering to this render target, so the content is not available");
            }
        }*/

        #endregion
    }
}
